/*
* Copyright (c) 2021 Talkweb Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include "tiot_state_api.h"
#include "tiot_sysdep_api.h"
#include "service_log.h"

typedef struct {
    int fd;
    service_sysdep_socket_type_t socket_type;
    char *host;
    char backup_ip[16];
    uint16_t port;
    uint32_t connect_timeout_ms;
} core_network_ctx_t;

void *service_sysdep_malloc(uint32_t size)
{
    return malloc(size);
}

void service_sysdep_free(void *ptr)
{
    free(ptr);
}

uint64_t service_sysdep_time(void)
{
    struct timeval time;

    memset(&time, 0, sizeof(struct timeval));
    gettimeofday(&time, NULL);

    return ((uint64_t)time.tv_sec * 1000 + (uint64_t)time.tv_usec / 1000);
}

void service_sysdep_sleep(uint64_t time_ms)
{
    usleep(time_ms * 1000);
}

void *service_sysdep_memset(void* dest, int val, int size)
{
    return memset(dest, val, size);
}

void *service_sysdep_memcpy(void *destin, void *source, unsigned n)
{
    return memcpy(destin, source, n);
}

int service_sysdep_memcmp(const void *str1, const void *str2, unsigned n)
{
    return memcmp(str1, str2, n);
}

static void *service_sysdep_network_init(void)
{
    core_network_ctx_t *network_ctx = calloc(sizeof(core_network_ctx_t), 1);
    if (network_ctx != NULL) {
        network_ctx->connect_timeout_ms = 10000;
        return network_ctx;
    }

    return NULL;

}

static int32_t service_sysdep_network_setopt(void *ctx, service_sysdep_network_option_t option, void *data)
{
    core_network_ctx_t *network_ctx = (core_network_ctx_t *)ctx;

    if (ctx == NULL || data == NULL) {
        return RET_PORT_INPUT_NULL_POINTER;
    }

    if (option >= SERVICE_SYSDEP_NETWORK_MAX) {
        return RET_PORT_INPUT_OUT_RANGE;
    }

    switch (option) {
        case SERVICE_SYSDEP_NETWORK_SOCKET_TYPE: {
            network_ctx->socket_type = *(service_sysdep_socket_type_t *)data;
        }
        break;
        case SERVICE_SYSDEP_NETWORK_HOST: {
            network_ctx->host = malloc(strlen(data) + 1);
            if (network_ctx->host == NULL) {
                log_warn("malloc failed\n");
                return RET_PORT_MALLOC_FAILED;
            }
            memset(network_ctx->host, 0, strlen(data) + 1);
            memcpy(network_ctx->host, data, strlen(data));
        }
        break;
        case SERVICE_SYSDEP_NETWORK_BACKUP_IP: {
            memcpy(network_ctx->backup_ip, data, strlen(data));
        }
        break;
        case SERVICE_SYSDEP_NETWORK_PORT: {
            network_ctx->port = *(uint16_t *)data;
        }
        break;
        case SERVICE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS: {
            network_ctx->connect_timeout_ms = *(uint32_t *)data;
        }
        break;
        default: {
            break;
        }
    }

    return RET_SUCCESS;
}

static int32_t _service_sysdep_network_connect(char *host, uint16_t port, int family, int socktype, int protocol,
        uint32_t timeout_ms, int *fd_out)
{
    int32_t res = RET_SUCCESS;
    int fd = 0, sock_option = 0;
    char service[6] = {0};
    struct addrinfo hints;
    struct addrinfo *addrInfoList = NULL, *pos = NULL;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = family; /* only IPv4 */
    hints.ai_socktype = socktype;
    hints.ai_protocol = protocol;
    hints.ai_flags = 0;
    snprintf(service, 6, "%d", port);

    res = getaddrinfo(host, service, &hints, &addrInfoList);
    if (res == 0) {
        for (pos = addrInfoList; pos != NULL; pos = pos->ai_next) {
            fd = socket(pos->ai_family, pos->ai_socktype, pos->ai_protocol);
            if (fd < 0) {
                log_error("create socket error\n");
                res = RET_PORT_NETWORK_SOCKET_CREATE_FAILED;
                continue;
            }

            res = fcntl(fd, F_GETFL);
            if (res != -1) {
                res = fcntl(fd, F_SETFL, sock_option | O_NONBLOCK);
            }

            if (res == -1) {
                /* block connect */
                if (connect(fd, pos->ai_addr, pos->ai_addrlen) == 0) {
                    *fd_out = fd;
                    res = RET_SUCCESS;
                    break;
                } else {
                    res = RET_PORT_NETWORK_CONNECT_FAILED;
                }
            } else {
                /* non-block connect */
                fd_set write_sets;
                struct timeval timeselect;

                FD_ZERO(&write_sets);
                FD_SET(fd, &write_sets);

                timeselect.tv_sec = timeout_ms / 1000;
                timeselect.tv_usec = timeout_ms % 1000 * 1000;

                if (connect(fd, pos->ai_addr, pos->ai_addrlen) == 0) {
                    *fd_out = fd;
                    res = RET_SUCCESS;
                    break;
                } else if (errno != EINPROGRESS) {
                    res = RET_PORT_NETWORK_CONNECT_FAILED;
                } else {
                    res = select(fd + 1, NULL, &write_sets, NULL, &timeselect);
                    if (res == 0) {
                        res = RET_MQTT_LOG_CONNECT_TIMEOUT;
                    } else if (res < 0) {
                        res = RET_PORT_NETWORK_CONNECT_FAILED;
                    } else {
                        if (FD_ISSET(fd, &write_sets)) {
                            res = connect(fd, pos->ai_addr, pos->ai_addrlen);
                            if ((res != 0 && errno == EISCONN) || res == 0) {
                                *fd_out = fd;
                                res = RET_SUCCESS;
                                break;
                            } else {
                                res = RET_PORT_NETWORK_CONNECT_FAILED;
                            }
                        }
                    }
                }
            }

            close(fd);
            log_error("connect error, errno: %d\n", errno);
        }
    } else {
        res = RET_PORT_NETWORK_DNS_FAILED;
    }

    if (res < 0) {
        log_error("fail to establish tcp\n");
    } else {
        log_debug("success to establish tcp, fd=%d\n", *fd_out);
        /*disable signal SIGPIPE, MSG_NOSIGNAL for common linux，  SO_NOSIGPIPE for macOS*/
        struct sockaddr_in loc_addr;
        socklen_t len = sizeof(sizeof(loc_addr));
        char buf[1024] = {0};
        memset(&loc_addr, 0, len);
        if (-1 == getsockname(*fd_out, (struct sockaddr *)&loc_addr, &len)) {// 获取socket绑定的本地address信息
            memset(buf, 0, sizeof(buf));
            snprintf(buf, sizeof(buf), "get socket name failed. errno: %d, error: %s", errno, strerror(errno));
            perror(buf);
            //safe_close(*fd_out);
            exit(-1);
        }

        if (loc_addr.sin_family == AF_INET) {// 打印信息
            log_debug("local port: %u\n", ntohs(loc_addr.sin_port));
        }
        res = RET_SUCCESS;
    }
    freeaddrinfo(addrInfoList);


    return res;
}

static int32_t _service_sysdep_network_tcp_establish(core_network_ctx_t *network_ctx)
{
    int32_t res = RET_SUCCESS;

    log_debug("establish tcp connection with server(host='%s', port=[%u])\n", network_ctx->host, network_ctx->port);

    res = _service_sysdep_network_connect(network_ctx->host, network_ctx->port,
                                       AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, network_ctx->connect_timeout_ms, &network_ctx->fd);
    if (res == RET_PORT_NETWORK_DNS_FAILED && strlen(network_ctx->backup_ip) > 0) {
        log_debug("using backup ip: %s\n", network_ctx->backup_ip);
        res = _service_sysdep_network_connect(network_ctx->backup_ip, network_ctx->port,
                                           AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, network_ctx->connect_timeout_ms, &network_ctx->fd);
    }

    return res;
}

static int32_t _service_sysdep_network_udp_server_establish(core_network_ctx_t *network_ctx)
{
    int32_t sockfd;
    struct sockaddr_in servaddr;
    int opt_val = 1;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        log_error("create socket error, errno: %d\n", errno);
        perror("create socket error");
        return RET_PORT_NETWORK_SOCKET_CREATE_FAILED;
    }

    if (0 != setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val))) {
        log_error("setsockopt(SO_REUSEADDR) falied, errno: %d\n", errno);
        perror("setsockopt(SO_REUSEADDR) error");
        close(sockfd);
        return RET_PORT_NETWORK_SOCKET_CONFIG_FAILED;
    }

    memset(&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(network_ctx->port);

    if (-1 == bind(sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in))) {
        log_error("bind(%d) falied, errno: %d\n", (int)sockfd, errno);
        perror("bind(%d) error");
        close(sockfd);
        return RET_PORT_NETWORK_SOCKET_BIND_FAILED;
    }

    network_ctx->fd = sockfd;
    log_debug("success to establish udp, fd=%d\n", (int)sockfd);

    return 0;
}

static int32_t service_sysdep_network_establish(void *ctx)
{
    if (ctx == NULL) {
        return RET_PORT_INPUT_NULL_POINTER;
    }

    core_network_ctx_t *network_ctx = (core_network_ctx_t *)ctx;
    log_debug("service_sysdep_network_establish host %s port %d, type %d\r\n", network_ctx->host, network_ctx->port,
           network_ctx->socket_type);

    if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_CLIENT) {
        if (network_ctx->host == NULL) {
            return RET_PORT_MISSING_HOST;
        }
        return _service_sysdep_network_tcp_establish(network_ctx);
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_SERVER) {
        return RET_PORT_TCP_SERVER_NOT_IMPLEMENT;
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_CLIENT) {
        if (network_ctx->host == NULL) {
            return RET_PORT_MISSING_HOST;
        }
        return  _service_sysdep_network_connect(network_ctx->host, network_ctx->port,
                                             AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, network_ctx->connect_timeout_ms, &network_ctx->fd);
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_SERVER) {
        return _service_sysdep_network_udp_server_establish(network_ctx);
    }

    log_error("unknown nwk type or tcp host absent\n");
    return RET_PORT_NETWORK_UNKNOWN_SOCKET_TYPE;
}

static int32_t _service_sysdep_network_recv(core_network_ctx_t *network_ctx, uint8_t *buffer, uint32_t len,
        uint32_t timeout_ms)
{
    int res = 0;
    int32_t recv_bytes = 0;
    ssize_t recv_res = 0;
    uint64_t timestart_ms = 0, timenow_ms = 0, timeselect_ms = 0;
    fd_set recv_sets;
    struct timeval timestart, timenow, timeselect;

    FD_ZERO(&recv_sets);
    FD_SET(network_ctx->fd, &recv_sets);

    /* Start Time */
    gettimeofday(&timestart, NULL);
    timestart_ms = timestart.tv_sec * 1000 + timestart.tv_usec / 1000;
    timenow_ms = timestart_ms;

    do {
        gettimeofday(&timenow, NULL);
        timenow_ms = timenow.tv_sec * 1000 + timenow.tv_usec / 1000;

        if (timenow_ms - timestart_ms >= timenow_ms ||
            timeout_ms - (timenow_ms - timestart_ms) > timeout_ms) {
            break;
        }

        timeselect_ms = timeout_ms - (timenow_ms - timestart_ms);
        timeselect.tv_sec = timeselect_ms / 1000;
        timeselect.tv_usec = timeselect_ms % 1000 * 1000;

        res = select(network_ctx->fd + 1, &recv_sets, NULL, NULL, &timeselect);
        if (res == 0) {
            /* printf("_service_sysdep_network_recv, nwk select timeout\n"); */
            continue;
        } else if (res < 0) {
            log_error("_service_sysdep_network_recv, res < 0, errno: %d\n", errno);
            perror("_service_sysdep_network_recv, nwk select failed: ");
            return RET_PORT_NETWORK_SELECT_FAILED;
        } else {
            if (FD_ISSET(network_ctx->fd, &recv_sets)) {
                recv_res = recv(network_ctx->fd, buffer + recv_bytes, len - recv_bytes, 0);
                if (recv_res == 0) {
                    log_error("_service_sysdep_network_recv, nwk connection closed\n");
                    return RET_PORT_NETWORK_RECV_CONNECTION_CLOSED;
                } else if (recv_res < 0) {
                    log_error("_service_sysdep_network_recv, recv_res < 0, errno: %d\n", errno);
                    perror("_service_sysdep_network_recv, nwk recv error: ");
                    if (errno == EINTR) {
                        continue;
                    }
                    return RET_PORT_NETWORK_RECV_FAILED;
                } else {
                    recv_bytes += recv_res;
                    /*
                    printf("recv_bytes: %d, len: %d, data:\n",recv_bytes,len);
                    int i = 0;
                    for(i = 0; i<recv_res; i++){
                        printf("%X",buffer[i]);
                    }
                    printf("\n");
                    */
                    if (recv_bytes == len) {
                        break;
                    }
                }
            }
        }
    } while (((timenow_ms - timestart_ms) < timeout_ms) && (recv_bytes < len));
    /* printf("%s: recv over\n",__FUNCTION__); */
    //printf("----recv:%s\n",buffer);
    return recv_bytes;
}

static int32_t _service_sysdep_network_udp_server_recv(core_network_ctx_t *network_ctx, uint8_t *buffer,
        uint32_t len, uint32_t timeout_ms, service_sysdep_addr_t *addr)
{
    int res;
    struct sockaddr_in cliaddr;
    socklen_t addr_len = sizeof(cliaddr);
    fd_set read_fds;
    struct timeval timeout = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};

    FD_ZERO(&read_fds);
    FD_SET(network_ctx->fd, &read_fds);

    res = select(network_ctx->fd + 1, &read_fds, NULL, NULL, &timeout);
    if (res == 0) {
        log_error("select timeout\n");
        return 0;
    } else if (res < 0) {
        log_error("_linux_nwk_udp_read select errno: %d\n", errno);
        perror("_linux_nwk_udp_read select error: ");
        return RET_PORT_NETWORK_SELECT_FAILED;
    }

    res = recvfrom(network_ctx->fd, buffer, len, 0, (struct sockaddr *)&cliaddr, &addr_len);
    if (res >= 0) {
        if (NULL != addr) {
            addr->port = ntohs(cliaddr.sin_port);
            strcpy((char *)addr->addr, inet_ntoa(cliaddr.sin_addr));
        }

        return res;
    } else {
        log_error("_linux_nwk_udp_read errno: %d\n", errno);
        perror("_linux_nwk_udp_read error: ");
        return RET_PORT_NETWORK_RECV_FAILED;
    }
}

static int32_t service_sysdep_network_recv(void *ctx, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
                                        service_sysdep_addr_t *addr)
{
    if (ctx == NULL || buffer == NULL) {
        return RET_PORT_INPUT_NULL_POINTER;
    }
    if (len == 0 || timeout_ms == 0) {
        return RET_PORT_INPUT_OUT_RANGE;
    }

    core_network_ctx_t *network_ctx = (core_network_ctx_t *)ctx;

    if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_CLIENT) {
        return _service_sysdep_network_recv(network_ctx, buffer, len, timeout_ms);
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_SERVER) {
        return RET_PORT_TCP_SERVER_NOT_IMPLEMENT;
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_CLIENT) {
        return _service_sysdep_network_recv(network_ctx, buffer, len, timeout_ms);
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_SERVER) {
        return _service_sysdep_network_udp_server_recv(network_ctx, buffer, len, timeout_ms, addr);
    }

    log_warn("warn unknown nwk type\n");

    return RET_PORT_NETWORK_UNKNOWN_SOCKET_TYPE;
}

int32_t _service_sysdep_network_send(core_network_ctx_t *network_ctx, uint8_t *buffer, uint32_t len,
                                  uint32_t timeout_ms)
{
    int res = 0;
    int32_t send_bytes = 0;
    ssize_t send_res = 0;
    uint64_t timestart_ms = 0, timenow_ms = 0, timeselect_ms = 0;
    fd_set send_sets;
    struct timeval timestart, timenow, timeselect;

    FD_ZERO(&send_sets);
    FD_SET(network_ctx->fd, &send_sets);

    /* Start Time */
    gettimeofday(&timestart, NULL);
    timestart_ms = timestart.tv_sec * 1000 + timestart.tv_usec / 1000;
    timenow_ms = timestart_ms;

    do {
        gettimeofday(&timenow, NULL);
        timenow_ms = timenow.tv_sec * 1000 + timenow.tv_usec / 1000;

        if (timenow_ms - timestart_ms >= timenow_ms ||
            timeout_ms - (timenow_ms - timestart_ms) > timeout_ms) {
            break;
        }

        timeselect_ms = timeout_ms - (timenow_ms - timestart_ms);
        timeselect.tv_sec = timeselect_ms / 1000;
        timeselect.tv_usec = timeselect_ms % 1000 * 1000;

        res = select(network_ctx->fd + 1, NULL, &send_sets, NULL, &timeselect);
        if (res == 0) {
            log_error("_service_sysdep_network_send, nwk select timeout\n");
            continue;
        } else if (res < 0) {
            log_error("_service_sysdep_network_send, errno: %d\n", errno);
            perror("_service_sysdep_network_send, nwk select failed: ");
            return RET_PORT_NETWORK_SELECT_FAILED;
        } else {
            if (FD_ISSET(network_ctx->fd, &send_sets)) {

#if defined(MSG_NOSIGNAL)
                send_res = send(network_ctx->fd, buffer + send_bytes, len - send_bytes, MSG_NOSIGNAL);
#elif defined(SO_NOSIGPIPE)
                send_res = send(network_ctx->fd, buffer + send_bytes, len - send_bytes, SO_NOSIGPIPE);
#endif
                if (send_res == 0) {
                    log_error("_service_sysdep_network_send, nwk connection closed\n");
                    return RET_PORT_NETWORK_SEND_CONNECTION_CLOSED;
                } else if (send_res < 0) {
                    log_error("_service_sysdep_network_send, errno: %d\n", errno);
                    perror("_service_sysdep_network_send, nwk recv error: ");
                    if (errno == EINTR) {
                        continue;
                    }
                    return RET_PORT_NETWORK_SEND_FAILED;
                } else {
                    send_bytes += send_res;
                    if (send_bytes == len) {
                        break;
                    }
                }
            }
        }
    } while (((timenow_ms - timestart_ms) < timeout_ms) && (send_bytes < len));

    return send_bytes;
}


int32_t _service_sysdep_network_udp_send(core_network_ctx_t *network_ctx, uint8_t *buffer, uint32_t len,
                                      uint32_t timeout_ms, service_sysdep_addr_t *addr)
{
    struct sockaddr_in cliaddr;
    fd_set write_fds;
    struct timeval timeout = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
    int res;

    if (addr == NULL) {
        log_error("invalid parameter addr\n");
        return RET_PORT_NETWORK_SEND_FAILED;
    }

    FD_ZERO(&write_fds);
    FD_SET(network_ctx->fd, &write_fds);

    res = select(network_ctx->fd + 1, NULL, &write_fds, NULL, &timeout);
    if (res == 0) {
        log_error("select timeout\n");
        return 0;
    } else if (res < 0) {
        log_error("_linux_nwk_udp_write select errno: %d\n", errno);
        perror("_linux_nwk_udp_write select error");
        return RET_PORT_NETWORK_SELECT_FAILED;
    }

    res = inet_aton((char *)addr->addr, &cliaddr.sin_addr);
    if (res < 0) {
        log_error("sys_nwk_write, addr error\r\n");
        return RET_PORT_NETWORK_SEND_FAILED;
    }

    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(addr->port);

    res = sendto(network_ctx->fd, buffer, len, 0, (struct sockaddr *)&cliaddr, sizeof(struct sockaddr_in));
    if (res < 0) {
        log_error("_linux_nwk_udp_write errno: %d\n", errno);
        perror("_linux_nwk_udp_write error");
        return RET_PORT_NETWORK_SEND_FAILED;
    }

    return res;
}

static int32_t service_sysdep_network_send(void *ctx, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
                                        service_sysdep_addr_t *addr)
{
    if (ctx == NULL || buffer == NULL) {
        log_error("invalid parameter\n");
        return RET_PORT_INPUT_NULL_POINTER;
    }
    if (len == 0 || timeout_ms == 0) {
        return RET_PORT_INPUT_OUT_RANGE;
    }

    core_network_ctx_t *network_ctx = (core_network_ctx_t *)ctx;

    if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_CLIENT) {
        return _service_sysdep_network_send(network_ctx, buffer, len, timeout_ms);
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_SERVER) {
        return RET_PORT_TCP_SERVER_NOT_IMPLEMENT;
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_CLIENT) {
        return _service_sysdep_network_send(network_ctx, buffer, len, timeout_ms);
    } else if (network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_SERVER) {
        return _service_sysdep_network_udp_send(network_ctx, buffer, len, timeout_ms, addr);
    }

    log_error("unknown nwk type\n");
    return RET_PORT_NETWORK_UNKNOWN_SOCKET_TYPE;
}

static int32_t service_sysdep_network_deinit(void **ctx)
{
    if (ctx == NULL || *ctx == NULL) {
        return RET_PORT_INPUT_NULL_POINTER;
    }

    core_network_ctx_t *network_ctx = *(core_network_ctx_t **)ctx;

    if ((network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_TCP_CLIENT ||
         network_ctx->socket_type == SERVICE_SYSDEP_SOCKET_UDP_CLIENT) && network_ctx->host != NULL) {
        close(network_ctx->fd);
    }

    if (network_ctx->host != NULL) {
        free(network_ctx->host);
        network_ctx->host = NULL;
    }

    if(network_ctx != NULL){
         free(network_ctx);
        *ctx = NULL;
    }

    return 0;
}

void service_sysdep_rand(uint8_t *output, uint32_t output_len)
{
    uint32_t rand_num;
    srand((unsigned)time(NULL));
    rand_num = rand() % 100;
    snprintf(output, output_len, "%d", rand_num);
}

void *service_sysdep_mutex_init(void)
{
    int res = 0;
    pthread_mutex_t *mutex;
    if (NULL == (mutex = (pthread_mutex_t *)calloc(sizeof(pthread_mutex_t), 1) )) {
        return NULL;
    }

    res = pthread_mutex_init(mutex, NULL);
    if (res != 0) {
        log_error("create mutex failed\n");
        free(mutex);
        return NULL;
    }

    return (void *)mutex;
}

void service_sysdep_mutex_lock(void *mutex)
{
    if(mutex == NULL){
        return;
    }
    int res = 0;

    res = pthread_mutex_lock((pthread_mutex_t *)mutex);
    if (res != 0) {
        log_error("lock mutex error: :%d\n", res);
    }
}

void service_sysdep_mutex_unlock(void *mutex)
{
    if(mutex == NULL){
        return;
    }
    int res = 0;

    res = pthread_mutex_unlock((pthread_mutex_t *)mutex);
    if (res != 0) {
        log_error("unlock mutex error : %d\n", res);
    }
}

void service_sysdep_mutex_deinit(void **mutex)
{
    if(mutex == NULL){
        return;
    }
    int res = 0;

    res = pthread_mutex_destroy(*(pthread_mutex_t **)mutex);
    if (res == 0) {
        free(*(pthread_mutex_t **)mutex);
        *mutex = NULL;
    }else{
        log_error("unlock mutex error (%d)\n", res);
    }
}

int service_task_create(void *task_id, void *attr, void* (*start_rtn)(void*), void* arg)
{
    return pthread_create((pthread_t *)task_id, (pthread_attr_t *)attr, start_rtn, arg);
}

int service_task_join(void *thread, void **retval)
{
    return pthread_join((pthread_t)thread, retval);
}

char* service_unique_identifier(void)
{
    return "talkweb001";
}

tiot_sysdep_portfile_t g_tiot_sysdep_portfile = {
    service_sysdep_malloc,
    service_sysdep_free,
    service_sysdep_time,
    service_sysdep_sleep,
    service_sysdep_memset,
    service_sysdep_memcpy,
    service_sysdep_memcmp,
    service_sysdep_network_init,
    service_sysdep_network_setopt,
    service_sysdep_network_establish,
    service_sysdep_network_recv,
    service_sysdep_network_send,
    service_sysdep_network_deinit,
    service_sysdep_rand,
    service_sysdep_mutex_init,
    service_sysdep_mutex_lock,
    service_sysdep_mutex_unlock,
    service_sysdep_mutex_deinit,
    service_task_create,
    service_task_join,
    service_unique_identifier,
};

